#ifndef __Smart__hpp
#define __Smart__hpp

#ifndef __Smart__debug
#define __Smart__debug false //By default, turn off debugging. :)
// Even if it's not implemented yet...
#endif

#include "Iterable.h"
#include "SmartObject.h"
//could be compounded to include all of iostream, but why bother? :P
#include <istream>
#include <ostream>
#include <cstring>

#ifndef SmartArray_DEBUG
#define SmartArray_Debug false //Default SmartArray_Debug to off. ( = 0 )
#endif

#if smart_DEBUG==true
#include <iostream>
#define log_mem_func(a) std::cout << typeid(*this).name() << "::" << a << " in " << __FILE__ << " at line " << __LINE__ << std::endl
#define log_func(a) std::cout << a << " in " << __FILE__ << " at line " << __LINE__ << std::endl
#else
#define log_mem_func(a)
#define log_func(a)
#endif

template<class Type>
class Reference
{
	private:
		Type* obj; //The item we are currently referencing. :)
	public:
		class NullReference	{};//Throw this when an attempt to read something that isn't there.
		Reference( Type* arg ):obj(NULL)
		{
			operator = ( arg );//again, simple enough.
		}
		Reference( Reference& ref ):obj(NULL)
		{
			operator = ( ref );//simple enough. copy.
		}
		Reference( void ):obj(NULL)
		{
			operator = ( (Type*)NULL ); // By default, we want a null-reference.
		}
		Reference& operator = ( Type* ptr ) // usually like so: reference= new object.
		{
			if ( obj )
			{
				if (--(obj->_count)==0) delete obj; // de-increment reference-count.
				obj = NULL; // we want no references to this at all.
				// However, functionality wouldn't be effected without this line.
				// It's just for concept.
			}
			if( obj = ptr ) // Assign either a null-reference,
				obj->_count++; // or an actual object.
			return *this;
		}
		Reference& operator = ( Reference& r)
		{
			return operator = ( r.obj );
		}
		~Reference()
		{
			SmartObject* ptr = (Type*)0; // Ensure that TYPE is in fact derived from SmartObject!
			operator = ( NULL );
		}
		Type& operator () () const
		{
			if(obj) return *obj;
			throw NullReference();
		}
		bool Is( const Reference& ref )
		{
			return obj == ref.obj;
		}
		bool Is( const SmartObject& ref)
		{
			return obj == &ref;
		}
};

template <class Type=SmartObject>
class SmartArray:virtual public Iterable,virtual public SmartObject
{
protected:
	void Allocate( unsigned int len )
	{
		mAlloc = newSize(len);
		unsigned int lSize = mAlloc * sizeof(Reference<Type>);
		Reference<Type>* temp = static_cast<Reference<Type>*>(memset(new char[lSize],0x00,lSize));
		memcpy(temp,mItems,(sizeof(Reference<Type>))*mUsed);
		if(mItems) delete reinterpret_cast<char*>(mItems); //Prevent un-nescesary destruction of things...
		mItems = temp;
	}
public:
	SmartObject_Type_Information("Smart Array")
	class Iterator
	{
	private:
		int numRemaining;
		Type* obj;
	public:
		Iterator(Type* t,int l):obj(t),numRemaining(l){}
		Iterator(const Iterator& i):obj(i.obj),numRemaining(i.numRemaining){}
		operator bool(){ return numRemaining > 0; }
		Iterator& operator ++(){ obj++; numRemaining--; return *this;}
		Iterator& operator ++(int a){ obj++; numRemaining--; return *this;}
		Type& operator()(){ return *obj; }
	};
	class Callback
	{
		public:
			virtual void operator ()(Type&,unsigned int) const = 0;
	};
	SmartArray(void):mItems(NULL){}
	SmartArray( const SmartArray<Type>& arr ):mItems(NULL)
	{
		Append( arr );
	}
	SmartArray( unsigned int d):mItems(NULL)
	{
		try{ Reserve(d); }
		catch( Iterable::UnneededAllocation ) {}
	}
	virtual ~SmartArray(void)
	{
		if( mItems ){
			for(unsigned int i=0; i<mUsed; i++) mItems[i] = NULL;
			delete reinterpret_cast<char*>(mItems);
		}
	}
	const SmartArray& ForEach( const  Callback& cb) const
	{
		for( unsigned int i=0; i<mUsed; i++)
			cb(mItems[i](),i);
		return *this;
	}
	SmartArray& Reserve( unsigned int d)
	{
		try{ Allocate(d); }
		catch( Iterable::UnneededAllocation ) {}
		return *this;
	}
	SmartArray& Append( Type* nItem )
	{
		try{ Allocate(1);}
		catch ( Iterable::UnneededAllocation ) {}
		mItems[mUsed++] = nItem;
		return *this;
	}
	SmartArray& Append( const Reference<Type>& nItem )
	{
		try{ Allocate(1);}
		catch ( Iterable::UnneededAllocation ) {}
		mItems[mUsed++] = nItem;
		return *this;
	}
	SmartArray& Append( SmartArray<Type>* aRef )
	{
		return Insert( aRef, -1);
	}
	SmartArray& Append( const SmartArray<Type>& aRef)
	{
		return Insert( aRef, -1);
	}
	SmartArray& Insert( Type* nItem, int aIndice)
	{
		try{ Allocate(1);}
		catch ( Iterable::UnneededAllocation ) {}
		aIndice = Iterable::Slice(0,aIndice).end(mUsed) + 1;
		Reference<Type>* oldItems = new Reference<Type>[ mUsed - aIndice ];
		for( unsigned int i = aIndice; i < mUsed; i++ ) oldItems[i - aIndice] = mItems[i];
		mItems[aIndice++] = nItem, mUsed++;
		for( unsigned int i = aIndice; i < mUsed; i++ ) mItems[i] = oldItems[i - aIndice];
		delete [] oldItems;
		return *this;
	}
	SmartArray& Insert( Reference<Type>&r, int aIndice)
	{
		return Insert(&r(),aIndice);
	}
	SmartArray& Insert( SmartArray<Type>* aRef, int aIndice )
	{
		SmartArray<Type>& ref = *aRef;
		try{ Allocate( ref.mUsed ); }
		catch( Iterable::UnneededAllocation ) {}
		if( mUsed)
		{
		aIndice = Iterable::Slice(0,aIndice).end(mUsed) + 1;
		Reference<Type>* oldItems = new Reference<Type>[ mUsed - aIndice ];
		for( unsigned int i = aIndice; i < mUsed; i++ )oldItems[i - aIndice] = mItems[i];
		for( unsigned int i = 0; i < ref.mUsed; i++ ) mItems[ aIndice + i ] = ref.mItems[i];
		for( unsigned int i = 0; i < mUsed - aIndice; i++ ) mItems[ i + ref.mUsed + aIndice ] = oldItems[ i ];
		mUsed += ref.mUsed;
		delete [] oldItems;
		}
		else
		{
			for( unsigned int i=0; i<ref.mUsed; i++)
				mItems[i] = ref.mItems[i];
		}
		return *this;
	}
	SmartArray& Insert( const SmartArray<Type>& aRef, int aIndice)
	{
		return Insert( ((SmartArray<Type>*)&aRef), aIndice);
	}
	SmartArray& Prepend( Type* nItem)
	{
		return Insert(nItem,0);
	}
	SmartArray& Prepend( Reference<Type>& r )
	{
		return Insert(r,0);
	}
	SmartArray& Prepend( SmartArray<Type>* arr )
	{
		return Insert(arr,0);
	}
	SmartArray& Empty(bool aKeepValid=true)
	{
		if(aKeepValid)
			for( unsigned int i = 0; i < mUsed; i++ )
			{
				try{ mItems[i](); }
				catch( typename Reference<Type>::NullReference& nr )
				{
					for( unsigned int ii = i; ii < mUsed - 1; ii++ )
					{
						mItems[ii] = mItems[ii+1];
					}
					mItems[--mUsed] = NULL;
				}
			}
		else
		{
			for( unsigned int i = 0; i<mUsed; i++ )
				mItems[i] = NULL;
			mUsed = mAlloc = 0;
			delete reinterpret_cast<char*>(mItems);
			mItems = NULL;
		}
		return *this;
	}
	const SmartArray operator [] ( const Slice& aSlice ) const
	{
		SmartArray ret;
		unsigned int lStart = aSlice.start(mUsed), lEnd = aSlice.end(mUsed);
		for(unsigned int i = lStart; i ^ lEnd; i++) ret.Append(mItems[i]);
		return ret;
	}
	Type& operator[] ( int aIndice) const
	{
		return mItems[Iterable::Slice(0,aIndice).end(mUsed)]();
	}
	unsigned int Length() const
	{
		unsigned int ret = 0;
		for( unsigned int i=0; i<mUsed; i++)
		{
			try
			{
				mItems[i]();
				ret++;
			}
			catch( typename Reference<Type>::NullReference ){}
		}
		return ret;
	}
	void LoadFromFile( std::istream& in)
	{
		Empty(false);//Ensure it's all taken care of.
		in.read((char*)&mUsed,4);
		for( int i=0; i<mUsed; i++)
			mItems[i]().LoadFromFile(in);
	}
	void ExportToFile( std::ostream& out)
	{
		out.write((char*)&mUsed, 4);
		for( int i=0; i<mUsed; i++)
			mItems[i]().ExportToFile(out);
	}
	bool Contains( const SmartObject& cont, bool equivelant=false)
	{
		for( int i=0; i<mUsed; i++)
		{
			if( mItems[i].Is(cont)) return true;
			try
			{
				if( equivelant && mItems[i]() == cont) return true;
			}
			catch( typename Reference<Type>::NullReference) {}
		}
		return false;
	}
	bool Contains( const Reference<Type>& cont, bool equivelant=false)
	{
		return Contains( cont(), equivelant);
	}
	const Iterator& Start()
	{
		return Iterator((Type*)&(mItems[0]),( int)mUsed);
	}
	SmartArray& Remove( const Reference<Type>& obj, bool all=true)
	{
		for( unsigned int i=0; i<mUsed; i++)
		{
			if( mItems[i].Is(obj))
			{
				mItems[i] = NULL;
				for( int ii=i, end=--mUsed; ii<end; ii++)
				{
					mItems[ii] = mItems[ii+1];
				}
				if( !all) break;
			}
		}
		return *this;
	}
private:
	Reference<Type>* mItems;
};

#endif